All files / src/app/admin/dev-tools/guides/[slug] page.tsx

0% Statements 0/117
100% Branches 0/0
0% Functions 0/1
0% Lines 0/117

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118                                                                                                                                                                                                                                           
'use client';

import { useState, useEffect, use } from 'react';
import Link from 'next/link';
import type { MarkdownDocument } from '@/lib/dev-tools/types';
import SafeMarkdown from '@/components/ui/SafeMarkdown';

interface PageProps {
  params: Promise<{ slug: string }>;
}

export default function GuideDetailPage({ params }: PageProps) {
  const { slug } = use(params);
  const [guide, setGuide] = useState<MarkdownDocument | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchGuide = async () => {
      try {
        const res = await fetch(`/api/admin/dev-tools/docs/guides/${slug}`);
        if (!res.ok) {
          if (res.status === 404) throw new Error('Guide not found');
          throw new Error('Failed to fetch guide');
        }
        const result = await res.json();
        // Handle both new wrapped format and legacy format
        const data = result.data ?? result;
        setGuide(data.guide);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Failed to load guide');
      } finally {
        setIsLoading(false);
      }
    };
    fetchGuide();
  }, [slug]);

  if (isLoading) {
    return (
      <div className="max-w-[1170px] mx-auto px-4 sm:px-7.5 xl:px-0">
        <div className="animate-pulse">
          <div className="h-8 bg-gray-200 rounded w-48 mb-4"></div>
          <div className="h-4 bg-gray-200 rounded w-full mb-2"></div>
          <div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
          <div className="h-4 bg-gray-200 rounded w-1/2"></div>
        </div>
      </div>
    );
  }

  if (error || !guide) {
    return (
      <div className="max-w-[1170px] mx-auto px-4 sm:px-7.5 xl:px-0">
        <nav className="mb-4">
          <ol className="flex items-center gap-2 text-sm text-gray-600">
            <li><Link href="/admin/dev-tools" className="hover:text-blue">Developer Tools</Link></li>
            <li>/</li>
            <li><Link href="/admin/dev-tools/guides" className="hover:text-blue">Guides</Link></li>
          </ol>
        </nav>
        <div className="bg-red-50 border border-red-200 rounded-lg p-4 text-red-700">
          {error || 'Guide not found'}
        </div>
      </div>
    );
  }

  return (
    <div className="max-w-[1170px] mx-auto px-4 sm:px-7.5 xl:px-0">
      {/* Breadcrumb */}
      <nav className="mb-4">
        <ol className="flex items-center gap-2 text-sm text-gray-600">
          <li><Link href="/admin/dev-tools" className="hover:text-blue">Developer Tools</Link></li>
          <li>/</li>
          <li><Link href="/admin/dev-tools/guides" className="hover:text-blue">Guides</Link></li>
          <li>/</li>
          <li className="text-dark font-medium truncate max-w-[200px]">{guide.title}</li>
        </ol>
      </nav>

      <div className="flex gap-8">
        {/* Main Content */}
        <div className="flex-1 min-w-0">
          <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
            <SafeMarkdown content={guide.content} className="prose prose-sm max-w-none" />
          </div>
        </div>

        {/* Sidebar - Table of Contents */}
        {guide.headings.length > 0 && (
          <div className="hidden lg:block w-64 flex-shrink-0">
            <div className="sticky top-6">
              <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
                <h3 className="font-semibold text-dark mb-3">Table of Contents</h3>
                <nav className="space-y-1">
                  {guide.headings.filter(h => h.level <= 3).map((heading, idx) => (
                    <a
                      key={idx}
                      href={`#${heading.id}`}
                      className={`block text-sm text-gray-600 hover:text-blue transition-colors ${
                        heading.level === 1 ? 'font-medium' :
                        heading.level === 2 ? 'pl-2' : 'pl-4'
                      }`}
                    >
                      {heading.text}
                    </a>
                  ))}
                </nav>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}